﻿using Xejen.Communication.Configuration;
using Xejen.Communication.Http.Abstract;
using Xejen.Communication.Http.Configuration;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace Xejen.Communication.Http
{
    /// <summary>
    /// 代表以<see langword="HttpWebRequest"/>驱动方式向远程服务器发起请求
    /// </summary>
    /// <creator>marc</creator>
    internal class HttpWebRequestInvoker : IHttpInvoker
    {
        /// <inheritdoc/>
        public HttpConfig Config { get; set; }

        /// <summary>
        /// 序列化选项
        /// </summary>
        protected JsonSerializerOptions JsonSerializerOptions => Config.JsonSerializerOptions;

        /// <summary>
        /// 令牌
        /// </summary>
        public string Token { get; set; }

        /// <summary>
        /// 请求头
        /// </summary>
        public NameValueCollection Headers { get; set; }

        /// <inheritdoc cref="HttpWebRequestInvoker"/>
        protected HttpWebRequestInvoker() { }

        ///// <inheritdoc cref="HttpWebRequestInvoker"/>
        //public HttpWebRequestInvoker(HttpConfig config, string token = default, NameValueCollection headers = default)
        //{
        //    Check.NotNull(config, nameof(config));

        //    Config = config;

        //    Token = token;
        //    Headers = headers;
        //}

        /// <inheritdoc cref="HttpWebRequestInvoker"/>
        public HttpWebRequestInvoker(HttpConfig config, NameValueCollection headers)
        {
            Check.NotNull(config, nameof(config));

            Config = config;

            Headers = headers;
        }

        /// <inheritdoc/>
        public async Task<HttpResponse> DeleteAsync(string uri, int timeout, CancellationToken cancellationToken)
        {
            HttpWebRequest request = CreateRequest(uri, "DELETE", timeout);

            try
            {
                using (WebResponse response = await request.GetResponseAsync())
                {
                    return await HandleResponseAsync(response);
                }
            }
            catch (WebException ex)
            {
                await HandleExceptionAsync(ex);
                throw;
            }
            catch
            {
                throw;
            }
        }

        /// <inheritdoc/>
        public async Task<HttpResponse> GetAsync(string uri, int timeout, CancellationToken cancellationToken)
        {
            HttpWebRequest request = CreateRequest(uri, "GET", timeout);

            try
            {
                using (WebResponse response = await request.GetResponseAsync())
                {
                    return await HandleResponseAsync(response);
                }
            }
            catch (WebException ex)
            {
                await HandleExceptionAsync(ex);
                throw;
            }
            catch
            {
                throw;
            }
        }

        /// <inheritdoc/>
        public async Task<HttpResponse> PostAsync(string uri, object data, int timeout, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            HttpWebRequest request = CreateRequest(uri, "POST", timeout);
            request.ContentType = Config.ContentType;

            using (Stream requestStream = await request.GetRequestStreamAsync())
            {
                string content = GetContent(data);
                byte[] requestData = Config.Encoding.GetBytes(content);
                await requestStream.WriteAsync(requestData, 0, requestData.Length);
            }

            try
            {
                using (WebResponse response = await request.GetResponseAsync())
                {
                    return await HandleResponseAsync(response);
                }
            }
            catch (WebException ex)
            {
                await HandleExceptionAsync(ex);
                throw;
            }
            catch
            {
                throw;
            }
        }

        /// <inheritdoc/>
        public async Task<HttpResponse> PutAsync(string uri, object data, int timeout, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            HttpWebRequest request = CreateRequest(uri, "PUT", timeout);
            request.ContentType = Config.ContentType;

            using (Stream requestStream = await request.GetRequestStreamAsync())
            {
                string content = GetContent(data);
                byte[] requestData = Config.Encoding.GetBytes(content);
                await requestStream.WriteAsync(requestData, 0, requestData.Length);
            }

            try
            {
                using (WebResponse response = await request.GetResponseAsync())
                {
                    return await HandleResponseAsync(response);
                }
            }
            catch (WebException ex)
            {
                await HandleExceptionAsync(ex);
                throw;
            }
            catch
            {
                throw;
            }
        }

        private string GetContent(object data)
        {
            //if (Config.ContentType.StartsWith("application/json"))
            //{
            //    return JsonSerializer.Serialize(data, Xejen.Communication.CommunicationSerializerOptions.Normalize);
            //}
            //else
            //{
                return data.ToString();
            //}
        }

        private HttpWebRequest CreateRequest(string uri, string method, int timeout)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
            request.Method = method;
            request.Timeout = timeout;
            //request.PreAuthenticate = true;

            //if (!string.IsNullOrEmpty(Token))
            //{
            //    request.Headers.Add("Authorization", $"Bearer {Token}");
            //}

            if (!(string.IsNullOrEmpty(Config.UserName) && string.IsNullOrEmpty(Config.Password)))
            {
                request.Credentials = new NetworkCredential(Config.UserName, Config.Password);
            }

            AddHeaderParameters(request, Headers);

            return request;
        }

        private void AddHeaderParameters(HttpWebRequest request, NameValueCollection parameters)
        {
            if (request == null)
                return;

            if (parameters == null)
                return;

            foreach (string key in parameters)
            {
                request.Headers.Add(key, parameters[key]);
            }
        }

        private async Task HandleExceptionAsync(WebException ex)
        {
            if (ex.Response != null)
            {
                using (var errorResponse = (HttpWebResponse)ex.Response)
                {
                    using (var reader = new StreamReader(errorResponse.GetResponseStream()))
                    {
                        string errorContent = await reader.ReadToEndAsync();
                        //throw new HttpRequestException(errorContent);
                        //throw new HttpRequestException($"HTTP Error: {errorResponse.StatusCode} - {errorContent}", ex);
                        throw new HttpRequestException($"HTTP请求失败。状态码：{(int)errorResponse.StatusCode} \n内容：{Environment.NewLine}{errorContent}");
                    }
                }
            }

            throw ex;
        }

        private async Task<HttpResponse> HandleResponseAsync(WebResponse response)
        {
            using (StreamReader reader = new StreamReader(response.GetResponseStream()))
            {
                string content = await reader.ReadToEndAsync();
                byte[] bytes = Config.Encoding.GetBytes(content);

                return HttpResponse.Success(bytes, encoding: Config.Encoding);
            }
        }
    }
}
